<template>
    <el-select v-model="modelValue" ref="selectRef" :disabled="disabled"
            :clearable="clearable" @on-change="onSelectChange" transfer transfer-class-name="tree-dropdown">
        <el-option v-for="item in optionsList" :value="item.value" :key="item.value" style="display: none">{{ item.title }}</el-option>
        <el-tree :data="treeData" @on-select-change="handleSelectChange"></el-tree>
    </el-select>
</template>
<script>
export default {
    name: 'SelectTree',
    components: {

    },
    props: {
        rawData: {
            type: Array,
            default () {
                return [];
            }
        },
        parentValue: {
            type: Number
        },
        selfValue: {
            type: Number
        },
        disabled: {
            type: Boolean,
            default () {
                return false;
            }
        },
        clearable: {
            type: Boolean,
            default () {
                return false;
            }
        }
    },
    watch: {
        /* 传入的参数rawData变化时，会立即触发这里的方法, 外部页面加载时，马上就会调用这个值方法*/
        rawData: {
            handler() {
                //console.log("select-tree's watch rawData:" + JSON.stringify(this.parentValue)); // null  
                this.treeData = this.makeTree(this.rawData, 0, [], null); /* 必须写在这里 */
            },
            deep: true
        },
        parentValue: {
            immediate: true, // 这句重要
            handler(newVal) {
                this.select(newVal);
            }
        },
        selfValue(newVal) {
            /* 弹出对话框时，已经决定了那些树结构是不能访问的 */
            this.treeData = this.makeTree(this.rawData, 0, [], newVal); /* 必须写在这里 */
            this.select(this.parentValue);
        }
    },
    data() {
        return {
            modelValue: null,
            optionsList: [],
            treeData: [],
            treeDataMapping: {}
        }
    },
    created() {},
    mounted() {

    },
    beforeDestroy() {

    },
    methods: {
        /**
         * 为了解决clearable
         */
        onSelectChange() {
            if(this.modelValue === undefined) {
                this.resetSelected();
                this.$emit("on-select", null);
            }
        },
        /* 每次鼠标选择值都会触发这个方法,data就是选中的值，对于单选选择组件来说，返回的是只有一个元素的数组，
         * 比如：[{"value":14,"title":"真帮","expand":true,"selected":true,"path":[1,4],"children":[],"nodeKey":4}]
         */
        handleSelectChange(data) {
            //console.log(JSON.stringify(data));
            if (data.length === 0) return;
            let value = data[0].value;
            let title = data[0].title;

            /* 选中值 */
            this.select(value);

            /* 经过很多次测试，只有这段代码才能在选择值后自动关闭下拉框，测试this.$refs并不是全局的，而是页面级的*/
            this.$refs.selectRef.hideMenu();
            this.$emit("on-select", value);
        },
        /* 选中值 */
        select(value) {
            this.resetSelected();
            if(value == null) {
                this.modelValue = value;
                return;
            }

            /* 得到指定值的归属路径 */
            let target = this.treeDataMapping[value];
            let pathArr = target.path;

            /* 必须确保不要修改原始数据，所以这了新增了一个空数组并复制了数据 */
            let frontValue = [];
            frontValue.push(...pathArr);
            frontValue.push(value);

            /*
             * 因为组件是由一个隐藏的Option组件和Tree组件组合而成，每次选中值，
             * 我们都只给Option值一个选项，并且设置成选中状态
             * 这样Option组件才是最轻量的
             */
            this.optionsList = [];
            this.optionsList.push({
                value: value,
                title: this.concatPath(frontValue)
            });

            /* 设置select-tree组件的值，这是关键的 */
            this.modelValue = value;

            /* Tree组件需要设置selected，才会在样式上有所反馈 */
            target.selected = true;
        },
        /* 将选中状态全部重置 */
        resetSelected() {
            for (let key in this.treeDataMapping) {
                let item = this.treeDataMapping[key];
                item.selected = false;
            }
        },
        /* 将普通的列表数据构建成Tree组件需要的数据，当disableKey有值的时候，不要展示其值的子孙 */
        makeTree(data, parentId, pathArr, disableKey) {
            parentId = parentId || 0;
            let list = [];
            /* [].map(); 基本用法跟forEach方法类似 */
            let _this = this;

            /* map的回调函数中支持return返回值；return的是啥，相当于把数组中的这一项变为啥
            （并不影响原来的数组，只是相当于把原数组克隆一份，把克隆的这一份的数组中的对应项改变了） */
            data.map(function(item) {
                if (item.parent_id === parentId) {
                    /* 找到了父亲，就重新构建新的item，有儿子的就继续找儿子 */
                    let itemCas = {};
                    itemCas.value = item.id;
                    itemCas.title = item.name;
                    itemCas.expand = true;
                    itemCas.selected = false;

                    itemCas.path = [];
                    itemCas.path.push(...pathArr);

                    /* 如果disableKey为null，则可以放开手脚构建完整的树 
                     * 如果当前访问的item不是disableKey，也可以放开手脚访问其子树和节点
                     */
                    if (!disableKey || itemCas.value !== disableKey) {
                        /* 修改的情况下，是不能把自己修改到自己的子孙目录下的 */
                        let newPath = [];
                        newPath.push(...pathArr);
                        newPath.push(itemCas.value);

                        /* 有儿子的就继续找儿子，并绑到children中 */
                        itemCas.children = _this.makeTree(data, item.id, newPath, disableKey);

                        list.push(itemCas); /* 子函数中list的值是不一样的 */
                        /* 给新的item构建索引，方便快速检索和修改 */
                        _this.treeDataMapping[itemCas.value] = itemCas;
                    }
                }
            });
            /* 对于根节点，返回的就是只有一个元素的数组 */
            return list;
        },
        /* 将[1, 2, 3] 转换成 目录1/目录2/目录3 */
        concatPath(pathArr) {
            let pathNameArr = [];
            for (let i = 0; i < pathArr.length; i++) {
                let item = this.treeDataMapping[pathArr[i]];
                pathNameArr.push(item.title);
            }
            return pathNameArr.join("/");
        }
    }
}
</script>
<style>
    .ivu-tree-empty {
        line-height: 30px;
        font-size: 12px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        padding-left: 8px;
        padding-right: 24px;
    }

    .tree-dropdown {
        /*max-width: 300px !important;*/
    }
</style>